package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"sync"
	"time"
)

func main1() {
	m := New(httpGet)
	s := time.Now()
	for _, url := range incomingUrls() {
		start := time.Now()
		value, err := m.Get(url)
		if err != nil {
			log.Fatal(err)
		}
		fmt.Printf("%s,%s size:%d \n", url, time.Since(start), len(value.([]byte)))
	}
	fmt.Printf("总耗时：%s", time.Since(s))
}

func main() {
	m := New(httpGet)
	s := time.Now()
	var n sync.WaitGroup
	for _, url := range incomingUrls() {
		n.Add(1)
		go func(url string) {
			start := time.Now()
			value, err := m.Get(url)
			if err != nil {
				log.Fatal(err)
			} else {
				fmt.Printf("url : [%s] take time : [%s] size : [%d] \n", url, time.Since(start), len(value.([]byte)))
			}
			n.Done()
		}(url)
	}
	n.Wait()
	fmt.Printf("总耗时：%s", time.Since(s))
}

func incomingUrls() []string {
	return []string{
		"http://www.baidu.com",
		"https://blog.csdn.net/u010886975/article/details/88169222",
		"https://www.jianshu.com/p/bed143a1c886",
		"http://www.baidu.com",
		"https://blog.csdn.net/u010886975/article/details/88169222",
		"https://www.jianshu.com/p/bed143a1c886",
	}
}

func httpGet(uri string) (interface{}, error) {
	rsp, err := http.Get(uri)
	if err != nil {
		return nil, err
	}
	defer rsp.Body.Close()
	return ioutil.ReadAll(rsp.Body)
}

type Memo struct {
	f     Func
	mu    sync.Mutex
	cache map[string]*entry
}

type entry struct {
	res   result
	ready chan struct{}
}

type Func func(key string) (interface{}, error)

type result struct {
	value interface{}
	err   error
}

func New(f Func) *Memo {
	return &Memo{f: f, cache: make(map[string]*entry)}
}

func (m *Memo) Get(uri string) (interface{}, error) {
	m.mu.Lock()
	e := m.cache[uri]
	if e == nil {
		log.Printf("获取%s数据", uri)
		e = &entry{ready: make(chan struct{})}
		m.cache[uri] = e
		m.mu.Unlock()
		e.res.value, e.res.err = m.f(uri)
		close(e.ready)
	} else {
		log.Printf("等待获取%s数据", uri)
		m.mu.Unlock()
		<-e.ready
	}
	return e.res.value, e.res.err
	//m.mu.Lock()
	//res, ok := m.cache[uri]
	//m.mu.Unlock()
	//if !ok {
	//	res.value,res.err = m.f(uri)
	//	m.mu.Lock()
	//	m.cache[uri] = res
	//	m.mu.Unlock()
	//}
	//return res.value,res.err
}
